Previous Book Contents Book Index Next

Inside Macintosh: Overview /
Chapter 7 - Dialog Boxes


Using Modeless Dialog Boxes

To display a modeless dialog box, you can create the dialog box by calling GetNewDialog. Then you can respond to user actions in the dialog box by intercepting dialog-related events in your main event loop and handling those events. The Dialog Manager calls the Control Manager to draw any controls you've put in the dialog box and handle user actions in them. If the dialog box contains any application-defined user items, you need to provide the Dialog Manager with a drawing procedure so that it knows how to draw the items. You also need to handle user actions for any such application-defined items yourself.

Creating a Modeless Dialog Box

You can create a modeless dialog box by calling GetNewDialog and passing it the resource ID of an appropriate 'DLOG' resource. The Venn Diagrammer application supports only one modeless dialog box, in which the user can set various application preferences. Venn Diagrammer displays that dialog box after the user chooses the Preferences command from the Venn menu.

iGetVennPrefs: 
   DoModelessDialog(rVennDPrefsDial, gPrefsDialog);
As you can see, Venn Diagrammer simply calls the application-defined procedure DoModelessDialog, passing it a resource ID specifying the dialog box to open and a global variable in which to return the dialog pointer created by GetNewDialog.
Listing 7-2 defines the DoModelessDialog procedure.

Listing 7-2 Creating a modeless dialog box

PROCEDURE DoModelessDialog (myKind: Integer; VAR myDialog: DialogPtr);
   VAR
      myPointer:  Ptr;
BEGIN
   IF myDialog = NIL THEN                 {the dialog box doesn't exist yet}
      BEGIN
         myPointer := NewPtr(sizeof(DialogRecord));
         IF myPointer = NIL THEN
            exit(DoModelessDialog);

         myDialog := GetNewDialog(myKind, myPointer, WindowPtr(-1));
         IF myDialog <> NIL THEN
            BEGIN
               DoSetupUserItems(myKind, myDialog);    {set up user items}
               DoSetupCtrlValues(myDialog);           {set up initial values}
            END;
      END
   ELSE
      BEGIN
         ShowWindow(myDialog);
         SelectWindow(myDialog);
         SetPort(myDialog);
      END;
END;
The DoModelessDialog procedure first determines whether the specified dialog box has already been created, by checking the value of the global variable passed to it. If the variable contains any value other than NIL, the dialog box already exists (but is perhaps hidden or obscured by other windows). If so, DoModelessDialog simply makes the dialog box visible (by calling ShowWindow), makes it the active window (by calling SelectWindow), and establishes it as the current graphics port (by calling SetPort).

If, however, the specified dialog box doesn't exist yet, then DoModelessDialog allocates memory for a new dialog record and (if successful) calls GetNewDialog, passing it the appropriate resource ID. If GetNewDialog returns successfully (as indicated by a returned dialog pointer whose value isn't NIL), DoModelessDialog then calls two application-defined routines, DoSetupUserItems and DoSetupCtrlValues, to tell the Dialog Manager how draw the user items in the dialog box and to set the correct initial values for the dialog box's radio buttons and checkboxes.

Setting Up Application-Defined Items

Whenever a modeless dialog box contains application-defined user items, you need to tell the Dialog Manager how to draw them. You do this by calling the Dialog Manager procedure SetDialogItem for each application-defined item in the dialog box.
Listing 7-3 shows the DoSetupUserItems procedure called by DoModelessDialog (defined in Listing 7-2).

Listing 7-3 Setting up application-defined dialog items

PROCEDURE DoSetupUserItems (myKind: Integer; VAR myDialog: DialogPtr);
   VAR
      myType:     Integer;
      myHand:     Handle;
      myRect:     Rect;
      count:      Integer;
      origPort:   GrafPtr;
BEGIN
   GetPort(origPort);
   SetPort(myDialog);

   CASE myKind OF
      rVennDPrefsDial: 
         FOR count := 1 TO kVennPrefsItemCount DO
            IF count IN [iExist1Icon..iExist4Icon, 
                           iEmpty1Icon..iEmpty4Icon] THEN
            BEGIN
               GetDialogItem(myDialog, count, myType, myHand, myRect);
               SetDialogItem(myDialog, count, myType, @DoUserItem, myRect);
            END;
      OTHERWISE
         ;
   END;

   SetPort(origPort);
END;
The DoSetupUserItems procedure simply selects the relevant application-defined items, retrieves information about each item (by calling GetDialogItem), and then calls SetDialogItem to associate a particular application-defined drawing procedure with each item. As you can see, the drawing procedure (DoUserItem) is the same for each user item in the Preferences dialog box. This is possible because the Dialog Manager passes the drawing procedure the dialog pointer and item number when it wants a particular item to be drawn. Listing 7-4 defines the Venn Diagrammer procedure that draws user items.

Listing 7-4 Drawing application-defined dialog items

PROCEDURE DoUserItem (myDialog: DialogPtr; myItem: Integer);
   VAR
      myType:     Integer;
      myHand:     Handle;
      myRect:     Rect;
      origPort:   GrafPtr;
BEGIN
   GetPort(origPort);
   SetPort(myDialog);

   GetDialogItem(myDialog, myItem, myType, myHand, myRect);

   IF myDialog = gPrefsDialog THEN
      CASE myItem OF
         iExist1Icon..iExist4Icon: 
            BEGIN
               DoPlotIcon(myRect, GetIcon(kExistID + myItem - iExist1Icon),
                           myDialog, srcCopy);
            END;
         iEmpty1Icon..iEmpty4Icon: 
            BEGIN
               DoPlotIcon(myRect, GetIcon(kEmptyID + myItem - iEmpty1Icon),
                           myDialog, srcCopy);
               FrameRect(myRect);
            END;
         OTHERWISE
            ;
      END; {CASE}

   SetPort(origPort);            {restore original port}
END;
The DoUserItem procedure is also fairly simple. It makes sure that the dialog pointer passed to it picks out the Preferences dialog box. Then it calls the application-defined procedure DoPlotIcon (defined in Listing 5-8 on page 101) to draw the appropriate part of an icon in the item rectangle. If the emptiness patterns are being drawn, DoUserItem also draws a box around the pattern (by calling FrameRect).

Handling User Actions in a Modeless Dialog Box

The Venn Diagrammer application calls its DoHandleDialogEvent function for each event it retrieves from the Event Manager. Its strategy is to determine if the returned event applies to a dialog box. If so, DoHandleDialogEvent handles the event and returns TRUE to indicate that it did so; otherwise, DoHandleDialogEvent just returns FALSE to indicate that it didn't handle the event. Listing 7-5 defines DoHandleDialogEvent. (See Listing 4-4 on page 77 to see when DoHandleDialogEvent is called.)

Listing 7-5 Handling events in a modeless dialog box

FUNCTION DoHandleDialogEvent (myEvent: EventRecord): Boolean;
   VAR
      eventHandled:     Boolean;       {did we handle the event?}
      myDialog:         DialogPtr;
      myItem:           Integer;
BEGIN
   eventHandled := FALSE;
   IF FrontWindow <> NIL THEN
      IF IsDialogEvent(myEvent) THEN
         IF DialogSelect(myEvent, myDialog, myItem) THEN
            BEGIN
               eventHandled := TRUE;
               SetPort(myDialog);

               IF myDialog = gPrefsDialog THEN
                  BEGIN
                     CASE myItem OF
                        iEmpty1Radio..iEmpty4Radio: 
                           gEmptyIndex := myItem;
                        iEmpty1Icon..iEmpty4Icon: 
                           gEmptyIndex := myItem - 4;
                        iExist1Radio..iExist4Radio: 
                           gExistIndex := myItem - iEmpty4Icon;
                        iExist1Icon..iExist4Icon: 
                           gExistIndex := myItem - (iEmpty4Icon + 4);
                        iGetNextRandomly: 
                           gStepRandom := NOT gStepRandom;
                        iAutoAdjust: 
                           gAutoAdjust := NOT gAutoAdjust;
                        iShowSchoolNames: 
                           gShowNames := NOT gShowNames;
                        iUseExistImport: 
                           gGiveImport := NOT gGiveImport;
                        iSaveVennPrefs: 
                           DoSavePrefs;
                        OTHERWISE
                           ;
                     END;

                     DoSetupCtrlValues(myDialog);        {update values}
                  END;
            END;

   DoHandleDialogEvent := eventHandled;
END;
The DoHandleDialogEvent function calls the Dialog Manager's IsDialogEvent function to determine whether at the time of the event the frontmost window is a dialog box. If not, then DoHandleDialogEvent just exits and returns the value FALSE. If, however, the event did occur while a dialog box was active, then the event might apply to that dialog box. To determine whether it does apply, DoHandleDialogEvent calls the Dialog Manager's DialogSelect function, which handles most of the events relating to a dialog box. For example, if the event is an update or activate event for the dialog box, DialogSelect updates or activates the dialog box and returns FALSE (to indicate that no further processing is required by the calling application).

If the event involves an enabled item in the dialog box, DialogSelect returns a function result of TRUE. In the myItem parameter, it returns the item number of the item selected by the user. In the myDialog parameter, it returns a pointer to the dialog record for the dialog box where the event occurred. In all other cases, the DialogSelect function returns FALSE. When DialogSelect returns TRUE, you should do whatever is appropriate as a response to the event involving that item in that particular dialog box; when it returns FALSE, you should do nothing.

The DoHandleDialogEvent function uses a very simple technique for handling user selections of items in the Preferences dialog box. As you can see, it sets the appropriate application global variables for clicks of the radio buttons, and it toggles the appropriate global variables for clicks of the checkboxes. Then DoHandleDialogEvent calls the application-defined procedure DoSetupCtrlValues to change the values of those controls, turning the radio buttons and checkboxes off or on, as appropriate. Listing 7-6 gives the definition of DoSetupCtrlValues.

Listing 7-6 Setting the state of radio buttons and checkboxes

PROCEDURE DoSetupCtrlValues (myDialog: DialogPtr);
   VAR
      count:      Integer;
      myType:     Integer;
      myHand:     Handle;
      myRect:     Rect;
      origPort:   GrafPtr;
BEGIN
   IF myDialog = NIL THEN
      exit(DoSetupCtrlValues);

   GetPort(origPort);               {save the current graphics port}
   SetPort(myDialog);               {always do this before drawing}
   ShowWindow(myDialog);

   IF myDialog = gPrefsDialog THEN
      BEGIN
         FOR count := 1 TO kVennPrefsItemCount DO
            BEGIN
               GetDialogItem(myDialog, count, myType, myHand, myRect);
               IF myType = ctrlItem + radCtrl THEN
                  CASE count OF
                     iExist1Radio..iExist4Radio: 
                        SetCtlValue(ControlHandle(myHand),
                           ORD(gExistIndex = count - (iExist1Radio - 1)));
                     iEmpty1Radio..iEmpty4Radio: 
                        SetCtlValue(ControlHandle(myHand),
                           ORD(gEmptyIndex = count - (iEmpty1Radio - 1)));
                  OTHERWISE
                     ;
                  END;
               IF myType = ctrlItem + chkCtrl THEN
                  CASE count OF
                     iGetNextRandomly: 
                        SetCtlValue(ControlHandle(myHand),
                                     ORD(gStepRandom = TRUE));
                     iShowSchoolNames: 
                        SetCtlValue(ControlHandle(myHand), 
                                     ORD(gShowNames = TRUE));
                     iUseExistImport: 
                        SetCtlValue(ControlHandle(myHand),
                                     ORD(gGiveImport = TRUE));
                     iAutoAdjust: 
                        SetCtlValue(ControlHandle(myHand),
                                     ORD(gAutoAdjust = TRUE));
                  OTHERWISE
                     ;
                  END;
            END;
      END;

   SetPort(origPort);               {restore the previous graphics port}
END;
The DoSetupCtrlValues procedure simply calls the Control Manager procedure SetCtlValue to set the value of each control in the dialog box according to the value of some global variable. This makes it easy to toggle checkboxes and to group radio buttons in such a way that exactly one radio button in each group is on.

IMPORTANT
The strategy for handling dialog box events described in this section might not be the best or most efficient strategy for your application. For a more complete discussion of handling dialog box events, see the chapter "Dialog Manager" in Inside Macintosh: Macintosh Toolbox Essentials.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
9 JUL 1996